package com.lykj.common.utils;

import com.alibaba.fastjson.JSONObject;
import org.apache.http.*;
import org.apache.http.client.CookieStore;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;


public class HttpClient {

	private static Logger logger = LoggerFactory.getLogger(HttpClient.class);

	private org.apache.http.client.HttpClient httpClient = null;
	private CookieStore cookieStore = null;

	// 编码格式。发送编码格式统一用UTF-8
	private static final String ENCODING = "UTF-8";
	// 设置连接超时时间，单位毫秒。
	private static final int CONNECT_TIMEOUT = 30000;
	// 请求获取数据的超时时间(即响应时间)，单位毫秒。
	private static final int SOCKET_TIMEOUT = 30000;

	public HttpClient() {
		List<Header> headerList = new ArrayList<Header>();
		headerList.add(new BasicHeader(HttpHeaders.ACCEPT, "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01"));
		headerList.add(new BasicHeader(HttpHeaders.ACCEPT_ENCODING, "gzip, deflate"));
		headerList.add(new BasicHeader(HttpHeaders.CACHE_CONTROL, "max-age=0"));
		headerList.add(new BasicHeader(HttpHeaders.CONNECTION, "keep-alive"));
		headerList.add(new BasicHeader(HttpHeaders.ACCEPT_LANGUAGE, "zh-CN,zh;q=0.9"));
		headerList.add(new BasicHeader(HttpHeaders.USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36"));
//		headerList.add(new BasicHeader(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded; charset=UTF-8"));
		cookieStore = new BasicCookieStore();
		HttpClientContext.create().setCookieStore(cookieStore);
		httpClient = HttpClients.custom().setDefaultHeaders(headerList)
			.setDefaultRequestConfig(RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD_STRICT).build())
			.setDefaultCookieStore(cookieStore).build();
	}

	public static void main(String[] args) throws Exception {
		HttpClient httpClient = new HttpClient();

//		Map<String, Object> headers = new HashMap<>();
//		Map<String, Object> params = new HashMap<>();
//		params.put("username", "13838268286");
//		params.put("password", "e19d5cd5af0378da05f63f891c7467af");
//		HttpClientResult result = httpClient.doPost("http://223.100.7.22:3220/customer/login/loginByPwd", headers, params);
//		System.out.println(result.getCode());
//		System.out.println(result.getContent());

		Map<String, Object> headers = new HashMap<>();
		headers.put("Token", "4a5178e8-1533-439b-9b38-af97aeca54ab");
		headers.put("UserID", "270000017");
//		headers.put("Content-Type","application/json;charset=UTF-8");//application/ms-excel
//		headers.put("Content-Type", "multipart/form-data; boundary=--------------------------569494157846966165135872");//MediaType.MULTIPART_FORM_DATA
		File file = new File("D:\\50.xlsx");
		FileInputStream fileInputStream = new FileInputStream(file);
		MultipartFile mFile = new MockMultipartFile(file.getName(), file.getName(), ContentType.APPLICATION_OCTET_STREAM.toString(), fileInputStream);

		HttpClientResult result = httpClient.doPostFormFile("http://223.100.7.22:3220/sale/trade/protocolTrade/import","file", mFile, headers, null);
		System.out.println(result.getCode());
		System.out.println(result.getContent());

	}

	/**
	 * 发送post请求
	 *
	 * @param url        路径
	 * @param jsonObject 参数(json类型)
	 * @param encoding   编码格式
	 * @return
	 * @throws ParseException
	 * @throws IOException
	 */
	public HttpClientResult doPost(String url, Map<String, Object> headers, JSONObject jsonObject, String encoding) throws Exception {
		System.out.println("请求地址>>>>>>>>>："+url);
		// 创建http对象
		HttpPost httpPost = new HttpPost(url);
		/**
		 * setConnectTimeout：设置连接超时时间，单位毫秒。
		 * setConnectionRequestTimeout：设置从connect Manager(连接池)获取Connection
		 * 超时时间，单位毫秒。这个属性是新加的属性，因为目前版本是可以共享连接池的。
		 * setSocketTimeout：请求获取数据的超时时间(即响应时间)，单位毫秒。 如果访问一个接口，多少时间内无法返回数据，就直接放弃此次调用。
		 */
		RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();
		httpPost.setConfig(requestConfig);
		// 设置请求头
		httpPost.setHeader(HttpHeaders.ACCEPT, "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01");
		httpPost.setHeader(HttpHeaders.ACCEPT_ENCODING, "gzip, deflate");
		httpPost.setHeader(HttpHeaders.CACHE_CONTROL, "max-age=0");
		httpPost.setHeader(HttpHeaders.CONNECTION, "keep-alive");
		httpPost.setHeader(HttpHeaders.ACCEPT_LANGUAGE, "zh-CN,zh;q=0.9");
		httpPost.setHeader(HttpHeaders.USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36");
		httpPost.setHeader(HttpHeaders.CONTENT_TYPE, "application/json; charset=UTF-8");
		// 封装请求头
		packageHeader(headers, httpPost);
		// 装填参数
		StringEntity s = new StringEntity(jsonObject.toString(), encoding);//"utf-8"
		s.setContentEncoding(new BasicHeader("Content-Type", "application/json"));
		//设置参数到请求对象中
		httpPost.setEntity(s);
		// 创建httpResponse对象
		CloseableHttpResponse httpResponse = null;
		try {
			// 执行请求并获得响应结果
			return getHttpClientResult(httpResponse, httpPost);
		} finally {
			// 释放资源
			release(httpResponse);
		}
	}

	/**
	 * 发送get请求；带请求头和请求参数
	 *
	 * @param url     请求地址
	 * @param headers 请求头集合
	 * @param params  请求参数集合
	 * @return
	 * @throws Exception
	 */
	public HttpClientResult doGet(String url, Map<String, Object> headers, Map<String, Object> params) throws Exception {
		System.out.println("请求地址>>>>>>>>>："+url);
		// 创建访问的地址
		URIBuilder uriBuilder = new URIBuilder(url);
		if (params != null) {
			Set<Map.Entry<String, Object>> entrySet = params.entrySet();
			for (Map.Entry<String, Object> entry : entrySet) {
				uriBuilder.setParameter(entry.getKey(), entry.getValue().toString());
			}
		}
		// 创建http对象
		HttpGet httpGet = new HttpGet(uriBuilder.build());
		/**
		 * setConnectTimeout：设置连接超时时间，单位毫秒。
		 * setConnectionRequestTimeout：设置从connect Manager(连接池)获取Connection
		 * 超时时间，单位毫秒。这个属性是新加的属性，因为目前版本是可以共享连接池的。
		 * setSocketTimeout：请求获取数据的超时时间(即响应时间)，单位毫秒。 如果访问一个接口，多少时间内无法返回数据，就直接放弃此次调用。
		 */
		RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();
		httpGet.setConfig(requestConfig);
		// 封装请求头
		packageHeader(headers, httpGet);
		// 创建httpResponse对象
		CloseableHttpResponse httpResponse = null;
		try {
			// 执行请求并获得响应结果
			return getHttpClientResult(httpResponse, httpGet);
		} finally {
			// 释放资源
			release(httpResponse);
		}
	}

	/**
	 * 发送post请求；带请求头和请求参数
	 *
	 * @param url     请求地址
	 * @param headers 请求头集合
	 * @param params  请求参数集合
	 * @return
	 * @throws Exception
	 */
	public HttpClientResult doPost(String url, Map<String, Object> headers, Map<String, Object> params) throws Exception {
		System.out.println("请求地址>>>>>>>>>："+url);
		// 创建http对象
		HttpPost httpPost = new HttpPost(url);
		/**
		 * setConnectTimeout：设置连接超时时间，单位毫秒。
		 * setConnectionRequestTimeout：设置从connect Manager(连接池)获取Connection
		 * 超时时间，单位毫秒。这个属性是新加的属性，因为目前版本是可以共享连接池的。
		 * setSocketTimeout：请求获取数据的超时时间(即响应时间)，单位毫秒。 如果访问一个接口，多少时间内无法返回数据，就直接放弃此次调用。
		 */
		RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();
		httpPost.setConfig(requestConfig);
		// 设置请求头
		httpPost.setHeader(HttpHeaders.ACCEPT, "*/*");
		httpPost.setHeader(HttpHeaders.ACCEPT_ENCODING, "gzip, deflate, br");
		httpPost.setHeader(HttpHeaders.CACHE_CONTROL, "max-age=0");
		httpPost.setHeader(HttpHeaders.CONNECTION, "keep-alive");
		httpPost.setHeader(HttpHeaders.ACCEPT_LANGUAGE, "zh-CN,zh;q=0.9");
		httpPost.setHeader(HttpHeaders.USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36");
//		httpPost.setHeader(HttpHeaders.CONTENT_TYPE, "application/json; charset=UTF-8");
		// 封装请求头
		packageHeader(headers, httpPost);
		// 封装请求参数
		packageParam(params, httpPost);
		// 创建httpResponse对象
		CloseableHttpResponse httpResponse = null;
		try {
			// 执行请求并获得响应结果
			return getHttpClientResult(httpResponse, httpPost);
		} finally {
			// 释放资源
			release(httpResponse);
		}
	}

	/**
	 * 发送post请求；带请求头和请求参数
	 *
	 * @param url     请求地址
	 * @param headers 请求头集合
	 * @param params  请求参数集合
	 * @return
	 * @throws Exception
	 */
	public HttpClientResult doDelete(String url, Map<String, Object> headers, Map<String, Object> params) throws Exception {
		System.out.println("请求地址>>>>>>>>>："+url);
		// 创建http对象
		HttpDelete httpDelete = new HttpDelete(url);
		/**
		 * setConnectTimeout：设置连接超时时间，单位毫秒。
		 * setConnectionRequestTimeout：设置从connect Manager(连接池)获取Connection
		 * 超时时间，单位毫秒。这个属性是新加的属性，因为目前版本是可以共享连接池的。
		 * setSocketTimeout：请求获取数据的超时时间(即响应时间)，单位毫秒。 如果访问一个接口，多少时间内无法返回数据，就直接放弃此次调用。
		 */
		RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();
		httpDelete.setConfig(requestConfig);
		// 设置请求头
		httpDelete.setHeader(HttpHeaders.ACCEPT, "*/*");
		httpDelete.setHeader(HttpHeaders.ACCEPT_ENCODING, "gzip, deflate, br");
		httpDelete.setHeader(HttpHeaders.CACHE_CONTROL, "max-age=0");
		httpDelete.setHeader(HttpHeaders.CONNECTION, "keep-alive");
		httpDelete.setHeader(HttpHeaders.ACCEPT_LANGUAGE, "zh-CN,zh;q=0.9");
		httpDelete.setHeader(HttpHeaders.USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36");
		// 封装请求头
		packageHeader(headers, httpDelete);
		// 封装请求参数
//		packageParam(params, httpDelete);
		// 创建httpResponse对象
		CloseableHttpResponse httpResponse = null;
		try {
			// 执行请求并获得响应结果
			return getHttpClientResult(httpResponse, httpDelete);
		} finally {
			// 释放资源
			release(httpResponse);
		}
	}

	/**
	 * 发送put请求；带请求头和请求参数
	 *
	 * @param url     请求地址
	 * @param headers 请求头集合
	 * @param params  请求参数集合
	 * @return
	 * @throws Exception
	 */
	public HttpClientResult doPut(String url, Map<String, Object> headers, Map<String, Object> params) throws Exception {
		System.out.println("请求地址>>>>>>>>>："+url);
		// 创建http对象
		HttpPut httpPut = new HttpPut(url);
		/**
		 * setConnectTimeout：设置连接超时时间，单位毫秒。
		 * setConnectionRequestTimeout：设置从connect Manager(连接池)获取Connection
		 * 超时时间，单位毫秒。这个属性是新加的属性，因为目前版本是可以共享连接池的。
		 * setSocketTimeout：请求获取数据的超时时间(即响应时间)，单位毫秒。 如果访问一个接口，多少时间内无法返回数据，就直接放弃此次调用。
		 */
		RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();
		httpPut.setConfig(requestConfig);
		// 设置请求头
		httpPut.setHeader(HttpHeaders.ACCEPT, "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01");
		httpPut.setHeader(HttpHeaders.ACCEPT_ENCODING, "gzip, deflate");
		httpPut.setHeader(HttpHeaders.CACHE_CONTROL, "max-age=0");
		httpPut.setHeader(HttpHeaders.CONNECTION, "keep-alive");
		httpPut.setHeader(HttpHeaders.ACCEPT_LANGUAGE, "zh-CN,zh;q=0.9");
		httpPut.setHeader(HttpHeaders.USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36");
//		httpPut.setHeader(HttpHeaders.CONTENT_TYPE, "application/json; charset=UTF-8");
		// 封装请求头
		packageHeader(headers, httpPut);
		// 封装请求参数
		packageParam(params, httpPut);
		// 创建httpResponse对象
		CloseableHttpResponse httpResponse = null;
		try {
			// 执行请求并获得响应结果
			return getHttpClientResult(httpResponse, httpPut);
		} finally {
			// 释放资源
			release(httpResponse);
		}
	}

	/**
	 * 以post方式调用第三方接口,以form-data 形式  发送 MultipartFile 文件数据
	 *
	 * @param url           post请求url
	 * @param multipartFile 文件
	 */
	public HttpClientResult doPostFormFile(String url, String fileParamName, MultipartFile multipartFile, Map<String, Object> headers, Map<String, Object> params) {
		System.out.println("请求地址>>>>>>>>>："+url);
		// 创建HttpPost实例
		HttpPost httpPost = new HttpPost(url);
		// 请求参数配置
		RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(60000).setConnectTimeout(60000).setConnectionRequestTimeout(10000).build();
		httpPost.setConfig(requestConfig);
		try {
			// 封装请求头
			packageHeader(headers, httpPost);
			// 封装请求参数
			packageParam(params, httpPost);

			MultipartEntityBuilder builder = MultipartEntityBuilder.create();
			builder.setCharset(StandardCharsets.UTF_8);
			builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
			String fileName = multipartFile.getOriginalFilename();
			// 文件流
			builder.addBinaryBody(fileParamName, multipartFile.getInputStream(), ContentType.MULTIPART_FORM_DATA, fileName);
			HttpEntity entity = builder.build();
			httpPost.setEntity(entity);

			// 创建httpResponse对象
			CloseableHttpResponse httpResponse = null;
			try {
				// 执行请求并获得响应结果
				return getHttpClientResult(httpResponse, httpPost);
			} finally {
				// 释放资源
				release(httpResponse);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * Description: 封装请求头
	 *
	 * @param params
	 * @param httpMethod
	 */
	public static void packageHeader(Map<String, Object> params, HttpRequestBase httpMethod) {
		// 封装请求头
		if (params != null) {
			Set<Map.Entry<String, Object>> entrySet = params.entrySet();
			for (Map.Entry<String, Object> entry : entrySet) {
				// 设置到请求头到HttpRequestBase对象中
				httpMethod.setHeader(entry.getKey(), entry.getValue().toString());
			}
		}
	}

	/**
	 * Description: 封装请求参数
	 *
	 * @param params
	 * @param httpMethod
	 * @throws UnsupportedEncodingException
	 */
	public static void packageParam(Map<String, Object> params, HttpEntityEnclosingRequestBase httpMethod)
		throws UnsupportedEncodingException {
		// 封装请求参数
		if (params != null) {
			List<NameValuePair> nvps = new ArrayList<NameValuePair>();
			Set<Map.Entry<String, Object>> entrySet = params.entrySet();
			for (Map.Entry<String, Object> entry : entrySet) {
				nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));
			}
			// 设置到请求的http对象中
			httpMethod.setEntity(new UrlEncodedFormEntity(nvps, ENCODING));
		}
	}

	/**
	 * Description: 获得响应结果
	 *
	 * @param httpResponse
	 * @param httpMethod
	 * @return
	 * @throws Exception
	 */
	public HttpClientResult getHttpClientResult(HttpResponse httpResponse, HttpRequestBase httpMethod) throws Exception {
		// 执行请求
		httpResponse = httpClient.execute(httpMethod);
		HashMap<String, String> cookies = new HashMap<>();
		Header[] headers = httpResponse.getHeaders("Set-Cookie");
		Arrays.asList(headers).forEach(x -> {
			String token = x.getValue()
				.substring(x.getValue().indexOf("=") + 1, x.getValue().indexOf(';'));
			String key = x.getValue()
				.substring(0, x.getValue().indexOf("="));
			cookies.put(key, token);
		});
		// 获取返回结果
		if (httpResponse != null && httpResponse.getStatusLine() != null) {
			String content = "";
			if (httpResponse.getEntity() != null) {
				content = EntityUtils.toString(httpResponse.getEntity(), ENCODING);
			}
			return new HttpClientResult(httpResponse.getStatusLine().getStatusCode(), content, cookies);
		}
		return new HttpClientResult(HttpStatus.SC_INTERNAL_SERVER_ERROR);
	}

	/**
	 * URL编码
	 *
	 * @param msg
	 * @return
	 */
	private String encodeUtf8(String msg) {
		try {
			return URLEncoder.encode(msg, "utf-8");
		} catch (Exception ex) {
			ex.printStackTrace();
			return msg;
		}
	}

	/**
	 * Description: 释放资源
	 *
	 * @param httpResponse
	 * @throws IOException
	 */
	public void release(CloseableHttpResponse httpResponse) throws IOException {
		// 释放资源
		if (httpResponse != null) {
			httpResponse.close();
		}

	}

	/**
	 * Description: 获得响应结果
	 *
	 * @param httpResponse
	 * @param httpClient
	 * @param httpMethod
	 * @return
	 * @throws Exception
	 */
	public static HttpClientResult getHttpClientResult(CloseableHttpResponse httpResponse,
													   CloseableHttpClient httpClient, HttpRequestBase httpMethod) throws Exception {
		// 执行请求
		httpResponse = httpClient.execute(httpMethod);
		// 获取返回结果
		if (httpResponse != null && httpResponse.getStatusLine() != null) {
			String content = "";
			if (httpResponse.getEntity() != null) {
				content = EntityUtils.toString(httpResponse.getEntity(), ENCODING);
			}
			return new HttpClientResult(httpResponse.getStatusLine().getStatusCode(), content);
		}
		return new HttpClientResult(HttpStatus.SC_INTERNAL_SERVER_ERROR);
	}

	/**
	 * Description: 释放资源
	 *
	 * @param httpResponse
	 * @param httpClient
	 * @throws IOException
	 */
	public static void release(CloseableHttpResponse httpResponse, CloseableHttpClient httpClient) throws IOException {
		// 释放资源
		if (httpResponse != null) {
			httpResponse.close();
		}
		if (httpClient != null) {
			httpClient.close();
		}
	}
}
